﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;

using Styx;
using Styx.Common;
using Styx.CommonBot;
using Styx.CommonBot.Profiles;
using Styx.Pathing;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using Styx.Plugins;	

namespace rndbuddy
{
    static class PlayerSecurity
    {
        static List<PlayerObject.Player> pList = new List<PlayerObject.Player>();

        public static void who_is_targeting_me(bool v=false)
        {
            try
            {
                var readList = ObjectManager.GetObjectsOfType<WoWPlayer>(false, false)
                                   //.Where(o => o.IsPlayer && o.Distance < 120)
                                   .ToList();

                foreach (var wpl in readList.Where(wpl => wpl != null))
                {
                    if (wpl == null)
                        continue;

                    if (!wpl.IsPlayer)
                        continue;
                   
                    if (wpl.IsTargetingMeOrPet)
                    {
                        rndbuddy.log("TargetInfo: " + wpl.Name + " targeting you! Distance: " + wpl.Distance.ToString());
                        
                        if (BotController.is_manual_botbase())
                            return;

                        server_pipe.report_targeted
                                (wpl.Name, wpl.Level, wpl.Faction.Name,
                                wpl.X, wpl.Y, wpl.Z, wpl.Distance);
                    }

                    if (v)
                        rndbuddy.log(wpl.Name + "\r\n\t Dist=" + wpl.Distance.ToString() + "\r\n\t targeting=" + wpl.IsTargetingMeOrPet.ToString() + ".");
                }
            }
            catch (Exception e)
            {
                rndbuddy.logsilent("Exception thrown in PlayerSecurity: " + e.Message);
                rndbuddy.logsilent("Stacktrace: " + e.StackTrace);
            }
        }

        public static void BNetWhisper(object sender, LuaEventArgs args)
        {
            if (!((string)args.Args[0]).StartsWith("OQ,0Z,"))
            {
                string message = args.Args[0].ToString();
                string from = args.Args[12].ToString();
                string type = args.ToString();
                server_pipe.log_chat(type, from, message);
            }
        }

        public static void Chat_Whisper(Styx.CommonBot.Chat.ChatWhisperEventArgs e)
        {
            if (e.Status == "GM")
            {
                //bad gm..
                rndbuddy.log("CRITICAL! GM Whisper recieved");
                if (Battlegrounds.IsInsideBattleground)
                {
                    Battlegrounds.LeaveBattlefield();
                }
                rndbuddy.forceQuit(true,"GM Whisper recieved! ("+e.Author+"): "+e.Message);
            }
            server_pipe.log_chat(e.EventName, e.Author, e.Message);

            //notify?
            if (settings.notify_on_events)
                server_pipe.notify("Whisper " + e.Author + ": " + e.Message);
        }

        public static void Chat_Channel(Chat.ChatChannelSpecificEventArgs e)
        {
            if (e.Message.StartsWith("OQ,"))
                return;
            server_pipe.log_chat("Channel " + e.ChannelName, e.Author, e.Message);
        }

        public static void Chat_Guild(Chat.ChatGuildEventArgs e)
        {
            server_pipe.log_chat("CHAT_MSG_GUILD", e.Author, e.Message);
        }

        public static void Chat_Party(Chat.ChatLanguageSpecificEventArgs e)
        {
            server_pipe.log_chat("CHAT_MSG_PARTY", e.Author, e.Message);
        }

        /* player following detection */
        public static void PlayerFollowDetection()
        {
            if (!settings.followers_detection)
                return;
            
            if (BotController.is_manual_botbase())
                return;

            //clear list of expired players
            if (pList.Any())
            {
                pList = pList.Where(x => DateTime.Now < x.LastSeen.AddSeconds(settings.watch_expireTime)).ToList();
            }

            //add new ones
            var readList = ObjectManager.GetObjectsOfType<WoWPlayer>(false, false)
                                .Where(o => o.IsPlayer && o.Distance < settings.watch_range)
                                .ToList();

            foreach (var wpl in readList.Where(wpl => wpl != null))
            {

                PlayerObject.Player pl = new PlayerObject.Player();
                pl.objPlayer = wpl;
                pl.FirstSeen = DateTime.Now;
                pl.LastSeen = DateTime.Now;
                pl.SinceFirstSeen = new TimeSpan();
                pl.DistanceSqr = (int)wpl.DistanceSqr;
                pl.LastFollowNotified = -1;
                pl.LastTargetNotified = -1;

                if (wpl.IsTargetingMeOrPet)
                {
                    pl.TargetedAt = DateTime.Now;
                    pl.SinceTarget = new TimeSpan();
                }

                DateTime lastseen = DateTime.Now;
                DateTime TargetedAt = DateTime.Now;
                DateTime firstseen = DateTime.Now;
                TimeSpan SinceFirstSeen = new TimeSpan();
                TimeSpan SinceTarget = new TimeSpan();
                int LastFollowNotified = -1;
                int LastTargetNotified = -1;
                int DistanceSqr = 0;
                bool skip = false;
                bool istargeting = false;
                int timesSeen = 1;

                if (pList.Any(x => x.objPlayer.Name == wpl.Name))
                {
                    foreach (PlayerObject.Player tpl in pList)
                    {
                        if (tpl.objPlayer.Name == wpl.Name)
                        {
                            lastseen = tpl.LastSeen;
                            TargetedAt = tpl.TargetedAt;
                            firstseen = tpl.FirstSeen;
                            SinceFirstSeen = tpl.SinceFirstSeen;
                            DistanceSqr = tpl.DistanceSqr;
                            istargeting = wpl.IsTargetingMeOrPet;
                            LastFollowNotified = tpl.LastFollowNotified;
                            LastTargetNotified = tpl.LastTargetNotified;
                            timesSeen = tpl.TimesSeen;

                            if (istargeting)
                            {
                                TargetedAt = tpl.TargetedAt;
                                SinceTarget = tpl.SinceTarget;
                            }

                        }
                        else
                        {
                            skip = true;
                        }

                    }

                    if (skip)
                    {
                        PlayerObject.Player existingplayer = new PlayerObject.Player();
                        existingplayer.objPlayer = wpl;
                        existingplayer.LastSeen = lastseen;
                        existingplayer.FirstSeen = firstseen;
                        existingplayer.SinceFirstSeen = SinceFirstSeen;
                        existingplayer.DistanceSqr = DistanceSqr;
                        existingplayer.TimesSeen = timesSeen;
                        existingplayer.LastFollowNotified = LastFollowNotified;
                        existingplayer.LastTargetNotified = LastTargetNotified;
                        if (TargetedAt != null)
                        {
                            existingplayer.TargetedAt = TargetedAt;
                            existingplayer.SinceTarget = SinceTarget;
                        }

                        PlayerObject.Player existingplayerupdate = new PlayerObject.Player();
                        existingplayerupdate.objPlayer = wpl;
                        existingplayerupdate.LastSeen = DateTime.Now;
                        existingplayerupdate.TimesSeen++;
                        existingplayerupdate.FirstSeen = firstseen;
                        existingplayerupdate.SinceFirstSeen = (DateTime.Now - firstseen);
                        existingplayerupdate.DistanceSqr = (int)wpl.DistanceSqr;
                        existingplayerupdate.LastFollowNotified = LastFollowNotified;
                        existingplayerupdate.LastTargetNotified = LastTargetNotified;
                        if (TargetedAt != null)  // update targeting information if previously targeted
                        {
                            existingplayerupdate.TargetedAt = TargetedAt;
                            existingplayerupdate.SinceTarget = (DateTime.Now - TargetedAt);
                        }

                        //targeting me?
                        if (wpl.IsTargetingMeOrPet)
                        {
                            //Was I targeted before?
                            if (TargetedAt != null)
                                existingplayer.TargetedAt = TargetedAt;
                            else
                                existingplayerupdate.TargetedAt = DateTime.Now;
                        }

                        //Removes previous entry
                        pList = pList.Where(x => x.objPlayer.Name != existingplayer.objPlayer.Name).ToList();

                        //Adds updated player
                        pList.Add(existingplayerupdate);
                    }

                }
                else
                {
                    pList.Add(pl); //welcome to the relationship buddy
                }
            }
        }

        public static void dump_followers()
        {
            int amt_following = pList.ToList().Count();
            rndbuddy.log("Follower Detection");
            rndbuddy.log("Followers: "+amt_following);
            
            if (amt_following>0)
            foreach (var p in pList)
            {
                rndbuddy.log(p.objPlayer.Name.ToString());
                rndbuddy.log("Seen for " + p.SinceFirstSeen.TotalSeconds.ToString() + " seconds");
                rndbuddy.log("**************************");
            }
        }

        public static void analyze_followers()
        {
            if (!settings.followers_detection)
                return;
            if (BotController.is_manual_botbase())
                return;

            var cpl = pList.Where(x => x.SinceFirstSeen.TotalSeconds > 100).ToList();
            if (cpl.Count > 0)
            {
                rndbuddy.logcrit("There are " + cpl.Count.ToString() + " Player(s) following us!");
                rndbuddy.forceQuit(true, "Players are following bot!");
            }
        }

        public static void kill_hb(string reason = "")
        {
            server_pipe.notify_kill(reason);
            Process.GetProcesses().Where(
                    p =>
                        p.ProcessName == "Honorbuddy"
                    )
                        .ToList()
                        .ForEach(p => p.Kill());
            Environment.Exit(0);
        }
    }
}

namespace PlayerObject
{
    public class Player
    {
        public WoWPlayer objPlayer { get; set; }
        public DateTime FirstSeen { get; set; }
        public TimeSpan SinceFirstSeen { get; set; }
        public DateTime LastSeen { get; set; }
        public DateTime TargetedAt { get; set; }
        public TimeSpan SinceTarget { get; set; }
        public int DistanceSqr { get; set; }
        public int LastFollowNotified { get; set; }
        public int LastTargetNotified { get; set; }
        public int TimesSeen { get; set; }
    }
}
